package com.gmrz.util;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.math.BigInteger;
import java.security.*;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.Set;

/**
 * Created by gongyuqiang on 2017/5/8.
 */
public class CryUtil {

    private static final Logger LOG = LogManager.getLogger(CryUtil.class);
    public static String getSHA(String str) {
        MessageDigest messageDigest = null;
        try {
            messageDigest = MessageDigest.getInstance("sha-1");
        } catch (NoSuchAlgorithmException e) {
            LOG.error("CryUtil getSHA error", e);
            throw new RuntimeException(e);
        }
        byte[] hash = messageDigest.digest(str.getBytes());
        return Hex.encodeHexString(hash);
    }

    // 加密
    public static byte[] encryptAES128(byte[] sSrc, String sKey) {
        byte[] raw = (sKey.substring(0, 16)).getBytes();
        try {
            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");

            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");//"算法/模式/补码方式"
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
            byte[] encrypted = cipher.doFinal(sSrc);
            return encrypted;
        } catch (Exception e) {
            LOG.error("CryUtil encryptAES128 error", e);
            throw new RuntimeException(e);
        }

    }

    // 解密
    public static byte[] decryptAES128(byte[] sSrc, String sKey){
        try {
            byte[] raw = (sKey.substring(0, 16)).getBytes();
            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, skeySpec);
            byte[] original = cipher.doFinal(sSrc);
            return original;
        } catch (Exception e) {
            LOG.error("CryUtil decryptAES128 error", e);
            throw new RuntimeException(e);
        }
    }

    public static String decryptMetaData(String metaDataCipherText) {
        try {

            String publicKey = getPublic_key();
            String[] sources = metaDataCipherText.split("\\*");
            String passwd_c = sources[0];
            String metaData_c = sources[1];
            byte[] xx = decryptByPublicKey(Base64Utils.decode(passwd_c), publicKey);
            String pass = new String(xx);
            String metaData = decrypt3DES(metaData_c, pass);
            return metaData;
        } catch (Exception e) {
            LOG.error("CryUtil decryptMetaData error,data:"+metaDataCipherText, e);
        }
        return null;
    }
    public static boolean verifyData(String data) {
        try {
            byte[] base64 = Base64.decodeBase64(data);
            String certJson = new String(base64);
            JsonObject jsonObject = new JsonParser().parse(certJson).getAsJsonObject();
            JsonElement code = jsonObject.get("code");
            JsonElement info = jsonObject.get("info");

            String publicKey = new String(getPublic_key());
            return verify(info.toString().getBytes(),publicKey, code.toString());
        } catch (Exception e) {
            LOG.error("CryUtil verifyData error", e);
        }
        return false;
    }

    public static boolean verify(byte[] data, String publicKey, String sign)
            throws Exception {
        byte[] keyBytes = Base64Utils.decode(publicKey);
        // 使用提供的 keyBytes 编码公有密钥创建 X509EncodedKeySpec对象
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        // 返回一个KeyFactory对象，它转换指定 rsa 算法的公钥
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        // 转换为公钥
        PublicKey publicK = keyFactory.generatePublic(keySpec);
        // 生成指定 MD5withRSA 签名算法的签名对象
        Signature signature = Signature.getInstance("MD5withRSA");
        // 使用公钥初始化签名对象进行验证
        signature.initVerify(publicK);
        // 使用指定的字节数组更新要签名的数据
        signature.update(data);
        // 验签， sign：签名
        return signature.verify(Base64Utils.decode(sign));
    }

    private static final int MAX_DECRYPT_BLOCK = 128;
    public static final String KEY_ALGORITHM = "RSA";

    public static byte[] decryptByPublicKey(byte[] encryptedData, String publicKey){
        try {
            byte[] keyBytes = Base64Utils.decode(publicKey);
            X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            Key publicK = keyFactory.generatePublic(x509KeySpec);
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.DECRYPT_MODE, publicK);
            int inputLen = encryptedData.length;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int offSet = 0;
            byte[] cache;
            int i = 0;
            while (inputLen - offSet > 0) {
                if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                    cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
                } else {
                    cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
                }
                out.write(cache, 0, cache.length);
                i++;
                offSet = i * MAX_DECRYPT_BLOCK;
            }
            byte[] decryptedData = out.toByteArray();
            out.close();
            return decryptedData;
        }catch (Exception e){
            LOG.error("CryUtil decryptByPublicKey error", e);
            throw new RuntimeException(e);
        }
    }

    public static String decrypt3DES(String datasource, String password) {
        try {
            String algorithm = "des" + "ede";
            byte[] bytePassword = password.getBytes();

            byte[] tripleDESKey = new byte[24];
            int k = 0;
            int i = 0;

            for (i = 0; i < 24; i++) {
                if (k >= bytePassword.length) {
                    tripleDESKey[i] = 0;
                    ;
                } else
                    tripleDESKey[i] = bytePassword[k];
                k++;
            }
            SecretKey deskey = new SecretKeySpec(tripleDESKey, algorithm);

            Cipher c1 = Cipher.getInstance("des" + "ede/ECB/PKCS5Padding");
            c1.init(Cipher.DECRYPT_MODE, deskey);
            Base64 BASE64 = new Base64();
            return new String(c1.doFinal(BASE64.decode(datasource)));
        } catch (Throwable e) {
            LOG.error("CryUtil decrypt3DES error", e);
            throw new IllegalArgumentException(e);
        }
    }

    private static String public_key = "";

    private static String getPublic_key() {
        if (Strings.isNullOrEmpty(public_key)) {
            BufferedReader bufferedReader = null;
            try {
                InputStream inputStream = CryUtil.class.getClassLoader().getResourceAsStream("/public.pem");
                InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
                bufferedReader = new BufferedReader(inputStreamReader);
                String read = "";
                while ((read = bufferedReader.readLine()) != null) {
                    public_key = public_key + read;
                }
            } catch (Exception e) {
                LOG.error("CryUtil getPublic_key error", e);
            } finally {
                if (null != bufferedReader) {
                    try {
                        bufferedReader.close();
                    } catch (IOException e) {
                        LOG.error("CryUtil getPublic_key bufferedReader close error", e);
                    }
                }
            }

        }
        return public_key;
    }


    public static byte[] getSHA256(byte[] bytes){
        try {
            MessageDigest messageDigest;
            messageDigest = MessageDigest.getInstance("SHA-256");
            messageDigest.update(bytes);
            return messageDigest.digest();
        }catch (Exception e){
            LOG.error("CryUtil getSHA256 error", e);
            throw new RuntimeException(e);
        }
    }

    public static final String ALGORITHM = "AES/ECB/PKCS7Padding";

    /**
     * @param datas 要被加密的字符串
     * @param key 加/解密要用的长度为32的字节数组（256位）密钥
     * @return byte[]  加密后的字节数组
     */
    public static byte[] Aes256Encode(byte[] datas, byte[] key) {
        try {
            LOG.debug("key:"+Convert.toBase64(key)+",data:"+Convert.toBase64(datas));
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); //生成加密解密需要的Key
            cipher.init(Cipher.ENCRYPT_MODE, keySpec);
            return cipher.doFinal(datas);
        }catch (Exception e){
            LOG.error("CryUtil Aes256Encode error", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * @param bytes 要被解密的字节数组
     * @param key   加/解密要用的长度为32的字节数组（256位）密钥
     * @return String  解密后的字符串
     */
    public static byte[] Aes256Decode(byte[] bytes, byte[] key){
        try {
            LOG.debug("key:"+Convert.toBase64(key)+",data:"+Convert.toBase64(bytes));
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); //生成加密解密需要的Key
            cipher.init(Cipher.DECRYPT_MODE, keySpec);
            return cipher.doFinal(bytes);
        }catch (Exception e){
            LOG.error("CryUtil Aes256Decode error", e);
            throw new RuntimeException(e);
        }
    }

    public static short getRsaPkcs1PaddingDta(byte[] inData,byte[] oDATA ,byte flag) {
        byte[] SHA256_DIGEST_INFO = {
                0x30,0x31,0x30,0x0d,0x06,0x09,0x60,(byte) 0x86,
                0x48,0x01,0x65,0x03,0x04,0x02,0x01,0x05,
                0x00,0x04,0x20};
            System.arraycopy(SHA256_DIGEST_INFO, (short)0, oDATA, (short)(0), (short)SHA256_DIGEST_INFO.length);
            System.arraycopy(inData, (short)0, oDATA, (short)(SHA256_DIGEST_INFO.length), (short)inData.length);
            return 0;
    }

    public static void test() throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        Set<String> sets = Security.getAlgorithms("Signature");
        for(String str : sets){
            if(str.contains("RSA")){
                Signature s = Signature.getInstance(str);
                System.out.println(s.getAlgorithm()+"-->"+s.getProvider().getName());
            }
        }

        String pubkey = "bqhEmc4cAtbUAxQBFH-lMl-kKC-wTlLPqykMs-_kGLDGapcg0p0iM4DoErrkwLC1xpmnEwl07JmPbo27tK6DbR4hLJWGQZtYc3tjZtc85s0zj-FdRPgUPKIA0ypSE76xIAr4JfO_IBkAmwJ5KPvymnfFZo3_gYoPW_2uUueiCOGsd_83558UXjL1da2YzWvXZjsmdxEBaOEMtEVCSc_RSi_BwC9QmgCFLaNPd3w57YUHULqTy0RoEe6UBEKyLTKewIV_czZh_Cf8BgJtoWvNKV18ovj_Ab_g20tx6HxB48oX0PQSRqLiDMxk9iOfuiFcnFiYv19QcI7tdzGnLh3APw";
        byte[] pubexp = {(byte)0x00,(byte)0x01,(byte)0x00,(byte)0x01};

        KeyFactory factory = KeyFactory.getInstance("RSA");
        BigInteger n = new BigInteger(1, Arrays.copyOfRange(Convert.fromBase64(pubkey), 0, 256));
        BigInteger e = new BigInteger(1, pubexp);
        byte[] nba = n.toByteArray();
        byte[] eba = e.toByteArray();
        RSAPublicKeySpec spec = new RSAPublicKeySpec(n, e);
        RSAPublicKey pub = (RSAPublicKey)factory.generatePublic(spec);


        String data = "3031300D06096086480165030402010500042037363C24A98EAB1B8B6100B62EFD69A639C8557F05DFD9D856AE4D8AABE57800";
        String sign = "IQ8l_-s2REXgrfYAdRe-3G79PaNqRFKyNxmzqzH38PjRwMCQ9g3y2Awl8BXaiTpY5yReXJpu_2LNrJASEJzt1Dd5SMY7Axv4HzG1IW0OxpJNpgxsVhzc2QB0bV0_N-QXyyxETROX6ykzLM0ssP6rfGI7XRdjJT3aigOTESxsjtJp3PrFpeJKUzKQDYBKNU1o649CO4XfNx8-T80fMjFHLLJBUqj0r0QgjocCJ-J8mNsB1tK8uycwi-3gvFvKfRCqf-gtamDCgHeo3jSfak1WM3RGb92F1nkVzte7DY56D6Wi4nhPXrupb6IH4hNkzWayWeF5KJd0SzU--Q_nG8pnKA";
        Signature sig = null;
        sig = Signature.getInstance("RSA","BC");

        sig.initVerify(pub);
        sig.update(Convert.fromHex(data));
        boolean f = sig.verify(Convert.fromBase64(sign));
        System.out.println("f:"+f);
    }

    public static void main(String[] args) throws Exception {
//        String s1 = "0QfVunWvoKO4oV96oqe+Mg==";
//        String key = "JV+Ue2MNkZiJcHSkU+Gqdg==";
//        //byte[] keyBytes = getSHA256(Convert.fromBase64(key));
//        //byte[] b = Arrays.copyOf(keyBytes, 16);
//        byte[] datas1 = Aes256Encode(Convert.fromBase64(s1), Convert.fromBase64(key));
//        String datas = new sun.misc.BASE64Encoder().encode(datas1);
//        //byte[] datas = Aes256Decode(datas1, b);
//
//
        test();

    }

    static String getHex(byte[] raw) {
        String arHex = "0123456789ABCDEF";

        StringBuilder hex = new StringBuilder(2 * raw.length);
        for (byte b : raw) {
            hex.append("0123456789ABCDEF".charAt((b & 0xF0) >> 4)).append("0123456789ABCDEF".charAt(b & 0xF));
        }
        return hex.toString();
    }
}